#include "server.h"
key_t shmkey, semkey, queuekey;
int shmid=-1, semid=-1, mid=-1;
pid_t my_client, worker, the_server;	
char *shm=NULL;

int main(int argc, char *argv[]) 
{
	int mret=0;
	shmkey = 6000;
	semkey = 6001;
	queuekey = ftok(".", 'B');
	
	struct sembuf operations[1];
	struct sigaction act;
	struct my_msgbuf msgs;
	unsigned short ones[1] = {1};

	if( sigemptyset(&act.sa_mask) == -1) {
		perror("Error: sigemptyset failed.  server.c line 20");
		exit(EXIT_FAILURE);
	}
	
	act.sa_flags = SA_RESTART;
	act.sa_handler = (void *)waitchildren;
	
	if(sigaction(SIGCHLD, &act, NULL) == -1) {
		perror("Error: sigaction failed.  server.c line 28");
		exit(EXIT_FAILURE);
	}
	
	//act.sa_handler = (void *)keylog_exit;
	if(sigaction(SIGQUIT, &act, NULL) == -1) {
		perror("Error: sigaction failed.  server.c line 34");
		_exit(EXIT_FAILURE);
	}
	if(sigaction(SIGINT, &act, NULL) == -1) {
		perror("Error: sigaction failed.  server.c line 38");
		_exit(EXIT_FAILURE);
	}
	
	if(atexit(server_exit) != 0) {
		perror("Failed to attach atexit().  server.c line 43.");
		_exit(EXIT_FAILURE);
	}

    semid = semget(semkey, 1, 0666 | IPC_CREAT);
    if (semid < 0) { 
		 perror("Error Creating Semaphore.  server.c line 49"); 
		 exit(-1); 
	}

    argument.array = ones;
    if (semctl(semid, 0, SETALL, argument) < 0) {
        perror("Error Getting Semaphore Memory.  server.c line 55"); 
		exit(-1);
	}

    if ((shmid=shmget(shmkey, SHMSZ, IPC_CREAT|0666))<0) {
        perror("Error Getting Shared Memory.  server.c line 60"); 
		exit(-1);
    }

    if ((shm=shmat(shmid, NULL, 0))==(char*) -1) {
		perror("Error Attaching Shared Memory.  server.c line 65"); 
		exit(-1);
    }

	if((mid=msgget(queuekey, 0666|IPC_CREAT))<0) {
		perror("Error: Msg Queue setup failed.  server.c line 70.");
		exit(-1);
	}

	strcpy(shm, "abcdefgh");
	shm[10] = 0;

	the_server = getpid();

	//All setup is done.
	
	while(1) {		
		//New Client Request
		mret = msgrcv(mid, (struct msgbuf*) &msgs, sizeof(msgs.msg), 1, 0666);
		if(errno==EIDRM) { exit(0); }
		if(errno==EINTR) { mret = msgrcv(mid, (struct msgbuf*) &msgs, sizeof(msgs.msg), 1, 0666); }
		if(mret<0) {
			perror("Error: Msg Queue recieve failed.  server.c line 87");
			exit(-1);
		}
		
		my_client = atoi(msgs.msg);

		srandom((unsigned int)time(NULL));

		if(my_client == -1) { printf("Game completion message recieved.\n"); break; }
		
		if((worker = fork()) == -1) {
			perror("Error: Fork failed.  server.c line 98");
			exit(-1);
		}
		if(worker) { continue; }

		if(!worker) {
			//worker code
			worker = getpid();
			msgs.mtype = my_client;
			sprintf(msgs.msg, "%d", worker);
		
			mret = msgsnd(mid, (struct msgbuf*) &msgs, strlen(msgs.msg)+1, 0);
			if(errno==EIDRM) { perror("Message Queue pre-maturely deleted"); exit(-1); }
			if(errno==EINTR) { 
				mret = msgsnd(mid, (struct msgbuf*) &msgs, strlen(msgs.msg)+1, 0);
				if(errno==EIDRM) { perror("Message Queue pre-maturely deleted"); exit(-1); }
			}
			if(mret<0) {
				perror("Error: Msg Queue send failed.  server.c line 116");
				exit(-1);
			}

			while(1) {
				mret = msgrcv(mid, (struct msgbuf*) &msgs, sizeof(msgs.msg), worker, 0666);
				if(errno==EIDRM) { exit(0); }
				if(errno==EINTR) { 
					mret = msgrcv(mid, (struct msgbuf*) &msgs, sizeof(msgs.msg), worker, 0666); 
					if(errno==EIDRM) { exit(0); }
				}
				if(mret<0) {
					perror("Error: Msg Queue recieve failed.  server.c line 128");
					exit(-1);
				}
				//get access to shared memory
				
				operations[0].sem_num = 0;
				operations[0].sem_op = -1;
				operations[0].sem_flg = 0;
				semop(semid, operations, 1);

				if(shm[10] == (char)0x8) {//game done					
					exit(0);
				}
				
				printf("Worker for %d Processing: %s\n", my_client, shm);
				int temp = random()%8;
				if(isupper(shm[temp])) {
					msgs.mtype = my_client;
					strcpy(msgs.msg, "TRY AGAIN");

					mret = msgsnd(mid, (struct msgbuf*) &msgs, strlen(msgs.msg)+1, 0);
					if(errno==EIDRM) { exit(0); }
					if(errno==EINTR) { 
						mret = msgsnd(mid, (struct msgbuf*) &msgs, strlen(msgs.msg)+1, 0); 
						if(errno==EIDRM) { exit(0); }
					}
					if(mret<0) {
						perror("Error: Msg Queue send failed.  server.c line 155");
						exit(-1);
					}
					operations[0].sem_num = 0;
					operations[0].sem_op = 1;
					operations[0].sem_flg = 0;
					semop(semid, operations, 1);
					continue;
				}
				if(shm[10] == (char)0x7) {
					printf("%d wins\n", my_client);
					sprintf(msgs.msg, "%c RECIEVED\n\tYou Win!", shm[temp]);
				} else {
					sprintf(msgs.msg, "%c RECIEVED", shm[temp]);
				}
				msgs.mtype = my_client;
				
				mret = msgsnd(mid, (struct msgbuf*) &msgs, strlen(msgs.msg)+1, 0);
				if(errno==EIDRM) { exit(0); }
				if(errno==EINTR) { 
					mret = msgsnd(mid, (struct msgbuf*) &msgs, strlen(msgs.msg)+1, 0); 
					if(errno==EIDRM) { exit(0); }
				}
				if(mret<0) {
					perror("Error: Msg Queue recieve failed.  server.c line 179");
					exit(-1);
				}
				shm[temp]=toupper(shm[temp]);
				shm[10]++;
				operations[0].sem_num = 0;
				operations[0].sem_op = 1;
				operations[0].sem_flg = 0;
				semop(semid, operations, 1);
				continue;
			}
		}	
	}
    exit(0);
}

void server_exit(void)
{	//Will be repeated by all workers...
	if(shm!=NULL) { shmdt(shm); }
	if(mid!=-1) { msgctl(mid, IPC_RMID, 0); }
	if(semid!=-1) { semctl(semid, 0, IPC_RMID); }
    if(shmid!=-1) { shmctl(shmid, IPC_RMID, 0); }
	return;
}

void waitchildren(int signum) {
	//No real way to check for error.  Once wait3 is done waiting for child it returns error code...
	while (wait3((int *)NULL, WNOHANG, (struct rusage *)NULL) > 0) {}
	server_exit();
}